<!DOCTYPE html>
<!-- saved from url=(0049)https://www.cnblogs.com/cubbychat/p/15023207.html -->
<html lang="zh-cn" style="--olcb-folder-code-block-max-height: 80vh; --cnb-code-bg: rgb(245, 245, 245); --cnb-code-font-size: 12px; --cnb-code-color: rgb(68, 68, 68);"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="referrer" content="origin-when-cross-origin">
    <meta name="description" content="ViewModel 为 Android 组件间实现数据共享提供了一种更松耦合的方式。同时在维护组件状态方面也更加便捷，再也不用采用繁琐的 save instance 的方式了。">
    <meta property="og:description" content="ViewModel 为 Android 组件间实现数据共享提供了一种更松耦合的方式。同时在维护组件状态方面也更加便捷，再也不用采用繁琐的 save instance 的方式了。">
    <meta http-equiv="Cache-Control" content="no-transform">
    <meta http-equiv="Cache-Control" content="no-siteapp">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园</title>
    <link rel="canonical" href="https://www.cnblogs.com/cubbychat/p/15023207.html">
    <link rel="shortcut icon" id="favicon" href="https://assets.cnblogs.com/favicon.svg?v=2" type="image/svg+xml">
    
    <style>#home :not(.cnblogs_code):not(.cnblogs_Highlighter)>pre:not([highlighted]):not([class*="brush:"]) code:not(.hljs), :not(.cnblogs_code):not(.cnblogs_Highlighter)>pre:not([highlighted]):not([class*="brush:"]) code:not(.hljs) {background: rgb(245, 245, 245);
        padding: 12px;
        border: 1px solid rgb(204, 204, 204);
        border-radius: 3px;
        border-color: transparent;
        color: rgb(68, 68, 68);
        font-family: "Courier New", sans-serif;
        font-size: 12px</style><link rel="stylesheet" href="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/blog-common.min.css">
    

    <link id="MainCss" rel="stylesheet" href="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/bundle-lessismoreright.min.css">
        <link id="highlighter-theme-cnblogs" type="text/css" rel="stylesheet" href="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/cnblogs.css">
    
    
    <link id="mobile-style" media="only screen and (max-width: 767px)" type="text/css" rel="stylesheet" href="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/bundle-lessismoreright-mobile.min.css">
    
    <link type="application/rss+xml" rel="alternate" href="https://www.cnblogs.com/cubbychat/rss">
    <link type="application/rsd+xml" rel="EditURI" href="https://www.cnblogs.com/cubbychat/rsd.xml">
    <link type="application/wlwmanifest+xml" rel="wlwmanifest" href="https://www.cnblogs.com/cubbychat/wlwmanifest.xml">
    <script>
        var currentBlogId = 694622;
        var currentBlogApp = 'cubbychat';
        var isLogined = false;
        var isBlogOwner = false;
        var skinName = 'LessIsMoreRight';
        var visitorUserId = '';
        var hasCustomScript = false;
        window.cb_enable_mathjax = false;
        window.mathEngine = 0;
        window.codeHighlightEngine = 1;
        window.enableCodeLineNumber = false;
        window.codeHighlightTheme = 'cnblogs';
        window.darkModeCodeHighlightTheme = 'vs2015';
        window.isDarkCodeHighlightTheme = false;
        window.isDarkModeCodeHighlightThemeDark = true;
        window.isDisableCodeHighlighter = false;
        window.enableCodeThemeTypeFollowSystem = false;
        window.enableMacStyleCodeBlock = false;
    </script>
        <script>
            window.currentPostId = 15023207;
            window.currentPostDateAdded = '2021-07-17 13:07';
        </script>
    <script src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/jquery-3.3.1.min.js.下载"></script>
    <script src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/blog-common.min.js.下载"></script><style>.medium-zoom-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  opacity: 0;
  transition: opacity 300ms;
  will-change: opacity;
}

.medium-zoom--opened .medium-zoom-overlay {
  cursor: pointer;
  cursor: zoom-out;
  opacity: 1;
}

.medium-zoom-image {
  cursor: pointer;
  cursor: zoom-in;
  /*
    The `transition` is marked as "!important" for the animation to happen
    even though it's overriden by another inline `transition` style attribute.

    This is problematic with frameworks that generate inline styles on their
    images (e.g. Gatsby).

    See https://github.com/francoischalifour/medium-zoom/issues/110
   */
  transition: transform 300ms cubic-bezier(0.2, 0, 0.2, 1) !important;
}

.medium-zoom-image--hidden {
  visibility: hidden;
}

.medium-zoom-image--opened {
  position: relative;
  cursor: pointer;
  cursor: zoom-out;
  will-change: transform;
}
</style><script id="hljs-script" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/highlight.min.js.下载" type="text/javascript" async=""></script>
    
<meta property="og:image" content="https://img2020.cnblogs.com/blog/2467709/202107/2467709-20210717130008393-85929462.png"></head>
<body class="skin-lessismoreright has-navbar hljs-engine">
    <a name="top"></a>
    <div id="top_nav" class="navbar forpc">
        <nav id="nav_main" class="navbar-main">
            <ul id="nav_left" class="navbar-list navbar-left">
                <li class="navbar-branding">
                    <a href="https://www.cnblogs.com/" title="开发者的网上家园" role="banner">
                        <img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/logo.svg" alt="博客园Logo">
                    </a>
                </li>
                <li><a href="https://cnblogs.vip/">会员</a></li>
                <li><a href="https://cnblogs.vip/store">周边</a></li>
                <li>
                    <a href="https://news.cnblogs.com/" onclick="countClicks(&#39;nav&#39;, &#39;skin-navbar-news&#39;)">新闻</a>
                </li>
                <li>
                    <a href="https://q.cnblogs.com/" onclick="countClicks(&#39;nav&#39;, &#39;skin-navbar-q&#39;)">博问</a>
                </li>
                    <li>
                        <a href="https://www.cnblogs.com/cmt/p/18172236" target="_blank">融资</a>
                    </li>
                <li><a href="https://market.cnblogs.com/" target="_blank">云市场</a></li>
            </ul>
            <ul id="nav_right" class="navbar-list navbar-right">
                <li>
                    <form id="zzk_search" class="navbar-search dropdown" action="https://zzk.cnblogs.com/s" method="get" role="search">
                        <input name="w" id="zzk_search_input" placeholder="代码改变世界" type="search" tabindex="3" autocomplete="off">
                        <button id="zzk_search_button" onclick="window.navbarSearchManager.triggerActiveOption()">
                            <img id="search_icon" class="focus-hidden" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/search.svg" alt="搜索">
                            <img class="hidden focus-visible" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/enter.svg" alt="搜索">
                        </button>
                        <ul id="navbar_search_options" class="dropdown-menu quick-search-menu">
                            <li tabindex="0" class="active" onclick="zzkSearch(event, document.getElementById(&#39;zzk_search_input&#39;).value)">
                                <div class="keyword-wrapper">
                                    <img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/search.svg" alt="搜索">
                                    <div class="keyword"></div>
                                </div>
                                <span class="search-area">所有博客</span>
                            </li>
                                    <li tabindex="1" onclick="zzkBlogSearch(event, &#39;cubbychat&#39;, document.getElementById(&#39;zzk_search_input&#39;).value)">
                                        <div class="keyword-wrapper">
                                            <img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/search.svg" alt="搜索">
                                            <div class="keyword"></div>
                                        </div>
                                        <span class="search-area">当前博客</span>
                                    </li>
                        </ul>
                    </form>
                </li>
                <li id="navbar_login_status" class="navbar-list">
                    <a class="navbar-user-info navbar-blog" href="https://i.cnblogs.com/EditPosts.aspx?opt=1" alt="写随笔" title="写随笔" style="display: none;">
                        <img id="new_post_icon" class="navbar-icon" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/newpost.svg" alt="写随笔">
                    </a>
                    <a id="navblog-myblog-icon" class="navbar-user-info navbar-blog" href="https://passport.cnblogs.com/GetBlogApplyStatus.aspx" alt="我的博客" title="我的博客" style="display: none;">
                        <img id="myblog_icon" class="navbar-icon" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/myblog.svg" alt="我的博客">
                    </a>
                    <a class="navbar-user-info navbar-message navbar-icon-wrapper" href="https://msg.cnblogs.com/" alt="短消息" title="短消息" style="display: none;">
                        <img id="msg_icon" class="navbar-icon" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/message.svg" alt="短消息">
                        <span id="msg_count" style="display: none"></span>
                    </a>
                    <a id="navbar_lite_mode_indicator" data-current-page="blog" style="display: none" href="javascript:void(0)" alt="简洁模式" title="简洁模式启用，您在访问他人博客时会使用简洁款皮肤展示">
                        <img class="navbar-icon" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/lite-mode-on.svg" alt="简洁模式">
                    </a>
                    <div id="user_info" class="navbar-user-info dropdown" style="display: none;">
                        <a class="dropdown-button" href="https://home.cnblogs.com/">
                            <img id="user_icon" class="navbar-avatar" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/avatar-default.svg" alt="用户头像">
                        </a>
                        <div class="dropdown-menu">
                            <a id="navblog-myblog-text" href="https://passport.cnblogs.com/GetBlogApplyStatus.aspx">我的博客</a>
                            <a href="https://home.cnblogs.com/">我的园子</a>
                            <a href="https://account.cnblogs.com/settings/account">账号设置</a>
                            <a href="javascript:void(0)" id="navbar_lite_mode_toggle" title="简洁模式会使用简洁款皮肤显示所有博客">
    简洁模式 <img id="navbar_lite_mode_on" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/lite-mode-check.svg" class="hide" style="display: none;"><span id="navbar_lite_mode_spinner" class="hide">...</span>
</a>
                            <a href="javascript:void(0)" onclick="account.logout();">退出登录</a>
                        </div>
                    </div>
                    <a class="navbar-anonymous" href="https://account.cnblogs.com/signup" style="display: inline;">注册</a>
                    <a class="navbar-anonymous" href="javascript:void(0);" onclick="account.login()" style="display: inline;">登录</a>
                </li>
            </ul>
        </nav>
    </div>

    

    <div id="home">
    <div id="header">
        <div id="blogTitle">
            <div class="title"><a id="Header1_HeaderTitle" class="headermaintitle HeaderMainTitle" href="https://www.cnblogs.com/cubbychat">码农小舍</a>
</div>
<div class="subtitle"></div>

        </div>
        <div id="navigator">
            
<ul id="navList">
    <li id="nav_sitehome"><a id="blog_nav_sitehome" class="menu" href="https://www.cnblogs.com/">
博客园</a>
</li>
    <li id="nav_myhome">
<a id="blog_nav_myhome" class="menu" href="https://www.cnblogs.com/cubbychat/">
首页</a>
</li>
    <li id="nav_newpost">

<a id="blog_nav_newpost" class="menu" href="https://i.cnblogs.com/EditPosts.aspx?opt=1">
新随笔</a>
</li>
    <li id="nav_contact">
<a id="blog_nav_contact" class="menu" href="https://msg.cnblogs.com/send/%E7%A0%81%E5%86%9C%E5%B0%8F%E8%88%8D">
联系</a></li>
    <li id="nav_rss">
<a id="blog_nav_rss" class="menu" href="javascript:void(0)" data-rss="https://www.cnblogs.com/cubbychat/rss/">
订阅</a></li>
    <li id="nav_admin">
<a id="blog_nav_admin" class="menu" href="https://i.cnblogs.com/">
管理</a>
</li>
</ul>

            <div class="blogStats">
                
<span id="stats_post_count">随笔 - 1&nbsp;</span>
<span id="stats_article_count">文章 - 0&nbsp;</span>
<span id="stats_comment_count">评论 - 0&nbsp;</span>
<span id="stats_total_view_count">阅读 - 
<span title="总阅读数: 2039">
2039</span></span>
            </div>
        </div>
    </div>
    <div id="main">
        <div id="mainContent">
            <div class="forFlow">
                <div id="post_detail">
    <div id="topics">
        <div class="post">
            <h1 class="postTitle">
                <a id="cb_post_title_url" class="postTitle2 vertical-middle" href="https://www.cnblogs.com/cubbychat/p/15023207.html" title="发布于 2021-07-17 13:07">
    <span role="heading" aria-level="2">Android mvvm框架之ViewModel原理</span>
    

</a><button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button>

            </h1>
            <div class="clear"></div>
            <div class="postBody">
                    <div id="cnblogs_post_description" style="display: none">
        
        ViewModel 为 Android 组件间实现数据共享提供了一种更松耦合的方式。同时在维护组件状态方面也更加便捷，再也不用采用繁琐的 save instance 的方式了。
    </div>
<div id="cnblogs_post_body" class="blogpost-body cnblogs-markdown">
<p><strong>目录</strong></p>
<ul>
<li><strong>1 什么是 ViewModel</strong>
<ul>
<li>1.1 先考虑两个场景</li>
<li>1.2 缺点</li>
<li>1.3 特别说明</li>
<li>1.4 ViewModel 解决的问题</li>
</ul>
</li>
<li><strong>2 ViewModel 实现原理</strong>
<ul>
<li>2.1 ViewModel 类</li>
<li>2.2 ViewModel 的构造过程</li>
</ul>
</li>
<li><strong>3 ViewModel 与配置无关的原理（与宿主 Controller 俱生俱灭）</strong>
<ul>
<li>3.1 ViewModelStore 树</li>
<li>3.2 系统级的配置无关支持</li>
</ul>
</li>
<li><strong>4 FragmentActivity 中的 ViewModel 生命周期</strong></li>
<li><strong>5 多 Controller 共享 ViewModel</strong></li>
<li><strong>6 关于工厂模式的一点思考</strong></li>
</ul>
<br>
<br>
<h3 id="1-什么是-viewmodel">1 什么是 ViewModel<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h3>
<h4 id="11-先考虑两个场景">1.1 先考虑两个场景<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<ul>
<li>场景一：我们开发的 APP 可以转屏，转屏后将触发 Controller(Activity or Fragment) 的重建，为了维护转屏前后数据的一致性，我们要么将需要维护的数据以 Bundle 的形式在 onSaveInstance 中保存，必要的时候需要对复合数据实现繁琐的 Parceable 接口，如果数据量太大则我们必须将数据持久化，在转屏后重新拉取数据（from database or networks）；</li>
<li>场景二：我们的 Activity 中同时维护了多个 Fragment，每个 Fragment 需要共享一些数据，传统的做法是由宿主 Activity 持有共享数据，并暴露数据获取接口给各个寄生 Fragment。</li>
</ul>
<h4 id="12-缺点">1.2 缺点<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p>随着业务规模的扩大，以上的两种场景在传统实现方法中显得越来越繁琐且不易维护，且数据模块不易独立进行测试。</p>
<h4 id="13-特别说明">1.3 特别说明<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p>关于场景一，同样的场景还适用于各种配置相关的信息发生变化的情况，比如键盘、系统字体、语言区域等，它们的共同作用是都会导致当前 Controller 的重建。</p>
<h4 id="14-viewmodel-解决的问题">1.4 ViewModel 解决的问题<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p>ViewModel 是 android 新的 mvvm 框架的一部分，它的出现就是为了解决以上两个场景中数据与 Controller 耦合过度的问题。其 <strong>基本原理</strong> 是：<em>维护一个与配置无关的对象，该对象可存储 Controller 中需要的任何数据，其生命周期与宿主 Controller 的生命周期保持一致，不因 Controller 的重建而失效（注意：Controller 的重建仍然在 Controller 生命周期内，并不会产生一个新的生命周期，即 Controller 的 onDestroy 并不会调用）</em></p>
<p>这意味着无论是转屏还是系统字体变化等因配置变化产生的 Controller 重建都不会回收 ViewModel 中维护的数据，重建的 Controller 仍然可以从同一个 ViewModel 中通过获取数据恢复状态。</p>
<br>
<br>
<h3 id="2-viewmodel-实现原理">2 ViewModel 实现原理<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h3>
<h4 id="21-viewmodel-类">2.1 ViewModel 类<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p>如果大家去看一下 ViewModel 类的实现，会发现虽然它是一个 abstract 类，但是没有暴露任何外部可访问的方法，其预留的方法都是 package 访问权限的，<br>
其预留了一些数据清理工作的功能，推测可能是系统保留用作以后扩展，因为与我们对 ViewModel 原理的理解没有什么关联，我们暂且略过。</p>
<h4 id="22-viewmodel-的构造过程">2.2 ViewModel 的构造过程<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p>我们用一个结构图来剖析 ViewModel 的构造过程：</p>
<p><img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/2467709-20210717130008393-85929462.png" alt="" loading="lazy" class="medium-zoom-image"></p>
<p>如图所示：</p>
<ul>
<li>所有已经实例化的 ViewModel 都缓存在一个叫做 ViewModelStore 的封装对象中，其实质是一个 HashMap;</li>
<li>ViewModelStore 与具体的 Controller 绑定，并与宿主 Controller 俱生俱灭，所以这就解释了为何 ViewModel 与宿主 Controller 的生命周期是一样长了，因为缓存它的 ViewModelStore 与宿主 Controller 寿命相等；</li>
<li>获取 ViewModel 实例的过程委托给了一个叫做 ViewModelProvider 的工具类，它包含一个创建 ViewModel 的工厂类 Factory 和一个对 ViewModelStore 的引用；</li>
<li>总的构造过程为：先从 ViewModelStore 中获取缓存的 ViewModel，若没有缓存过则用 Facotry 实例化一个新的 ViewModel 并缓存，具体的过程分为 4 步，具体可参考图示。</li>
</ul>
<p><strong>本小节剩下部分分析源码，对于只关心原理的同学此部分可以略过：</strong></p>
<p>我们在获取 ViewModel 的时候，一般通过如下方式：</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs"><span class="hljs-comment">// 在 Controller（这里以 Fragment 为例）的 onCreate 方法中调用</span>
<span class="hljs-keyword">final</span> <span class="hljs-type">UserModel</span> <span class="hljs-variable">viewModel</span> <span class="hljs-operator">=</span> ViewModelProviders.of(<span class="hljs-built_in">this</span>).get(UserModel.class);
</code></pre>
<p>我们看一下 ViewModelProviders.of() 的实现：</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ViewModelProvider <span class="hljs-title function_">of</span><span class="hljs-params">(<span class="hljs-meta">@NonNull</span> Fragment fragment)</span> {
    <span class="hljs-keyword">return</span> of(fragment, <span class="hljs-literal">null</span>);
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ViewModelProvider <span class="hljs-title function_">of</span><span class="hljs-params">(<span class="hljs-meta">@NonNull</span> Fragment fragment, <span class="hljs-meta">@Nullable</span> Factory factory)</span> {
    <span class="hljs-type">Application</span> <span class="hljs-variable">application</span> <span class="hljs-operator">=</span> checkApplication(checkActivity(fragment));
    <span class="hljs-keyword">if</span> (factory == <span class="hljs-literal">null</span>) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }

    <span class="hljs-comment">// 最终用宿主 Controller 的 ViewModelStore 和一个 Factory 实例化一个</span>
    <span class="hljs-comment">// ViewModelProvider</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ViewModelProvider</span>(fragment.getViewModelStore(), factory);
}
</code></pre>
<p>我们再看一下 ViewModelProvider.get() 方法获取 ViewModel 实例的过程：</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs"><span class="hljs-keyword">public</span> &lt;T <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ViewModel</span>&gt; T <span class="hljs-title function_">get</span><span class="hljs-params">(<span class="hljs-meta">@NonNull</span> Class&lt;T&gt; modelClass)</span> {
    <span class="hljs-type">String</span> <span class="hljs-variable">canonicalName</span> <span class="hljs-operator">=</span> modelClass.getCanonicalName();
    <span class="hljs-keyword">if</span> (canonicalName == <span class="hljs-literal">null</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">IllegalArgumentException</span>(<span class="hljs-string">"Local and anonymous classes can not be ViewModels"</span>);
    }

    <span class="hljs-comment">// 我们看到了 ViewModel 在 ViewModelStore 中的 key 表示</span>
    <span class="hljs-keyword">return</span> get(DEFAULT_KEY + <span class="hljs-string">":"</span> + canonicalName, modelClass);
}

<span class="hljs-keyword">public</span> &lt;T <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ViewModel</span>&gt; T <span class="hljs-title function_">get</span><span class="hljs-params">(<span class="hljs-meta">@NonNull</span> String key, <span class="hljs-meta">@NonNull</span> Class&lt;T&gt; modelClass)</span> {

    <span class="hljs-comment">// 先检查缓存中是否存在</span>
    <span class="hljs-type">ViewModel</span> <span class="hljs-variable">viewModel</span> <span class="hljs-operator">=</span> mViewModelStore.get(key);
    <span class="hljs-keyword">if</span> (modelClass.isInstance(viewModel)) {
        <span class="hljs-comment">//noinspection unchecked</span>
        <span class="hljs-keyword">return</span> (T) viewModel;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">//noinspection StatementWithEmptyBody</span>
        <span class="hljs-keyword">if</span> (viewModel != <span class="hljs-literal">null</span>) {
            <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> log a warning.</span>
        }
    }

    <span class="hljs-comment">// 缓存中没有，通过 Factory 构造</span>
    <span class="hljs-keyword">if</span> (mFactory <span class="hljs-keyword">instanceof</span> KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } <span class="hljs-keyword">else</span> {
        viewModel = (mFactory).create(modelClass);
    }

    <span class="hljs-comment">// 新实例保存缓存</span>
    mViewModelStore.put(key, viewModel);
    <span class="hljs-comment">//noinspection unchecked</span>
    <span class="hljs-keyword">return</span> (T) viewModel;
}
</code></pre>
<br>
<br>
<h3 id="3-viewmodel-与配置无关的原理与宿主-controller-俱生俱灭">3 ViewModel 与配置无关的原理（与宿主 Controller 俱生俱灭）<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h3>
<p>上一节我们说到，ViewModel 之所以能够与宿主 Controller 保持生命周期一致，是因为存储它的 ViewModelStore 与宿主 Controller 生命周期一致。那么为什么 ViewModelStore 能够保持和 Controller 生命周期一致呢？</p>
<p>这里我们需要先理清 FragmentActivity 和其寄生的 Fragment 的 ViewModelStore 之间的关系：</p>
<h4 id="31-viewmodelstore-树">3.1 ViewModelStore 树<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p><img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/2467709-20210717130056954-527145710.png" alt="" loading="lazy" class="medium-zoom-image"></p>
<p>如图所示：</p>
<ul>
<li>每个 ViewModelStore 依附于其宿主 Controller，所以各个 Controller 的 ViewModelStore 组成一个树状的引用关系；</li>
<li>处于顶层的 ViewModelStore 依附于 FragmentActivity，它除了保存用户级的 ViewModel 以外，还保存其儿子 Fragment 的 FragmentManagerViewModel；</li>
<li>FragmentManagerViewModel 主要维护两个对象：所属 Fragment 的 ViewModelStore 和其儿子 Fragment 的 FragmentManagerViewModel 的引用，注意图中的红色部分，所有二级及以下的子孙 Fragment 都共用同一个父节点的 Child FragmentManagerModel，这样当父 Fragment 销毁的时候方便一次性清除其所有子 Fragment 共用的 FragmentManagerViewModel；</li>
<li>但是二级及以下的子孙 Fragment 的 ViewModelStore 都是独立的，一个 Fragment 自身的 ViewModel 变化应该不影响其兄弟节点的 ViewModel，所以可以推导出，它们共同的 FragmentManagerViewModel 应该是维护了一个保存各个子 Fragment 的 ViewModelStore 的容器，大家如果细看 FragmentManagerViewModel 的源代码，实际上就是这么做的。</li>
</ul>
<p><strong>所以，我们看到，处于顶层的 FragmentActivity 的 ViewModelStore 是一个超级 Store，它引用了所有的 ViewModels，包括自身的数据、所有子孙 Fragment 的 ViewModels，只要各子孙 Fragment 不清除自有 ViewModelStore，则所有的数据都维护在这棵 ViewModelStore 树中。</strong></p>
<p>那么在配置发生变化的时候，ViewModelStore 树如何保持不变呢？</p>
<h4 id="32-系统级的配置无关支持">3.2 系统级的配置无关支持<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h4>
<p>将 ViewModelStore 作为配置无关数据进行保持，在 FragmentActivity 中是这么做的：</p>
<p><img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/2467709-20210717130122655-2077499730.png" alt="" loading="lazy"></p>
<p>是的，流程就是这么简单，只需要将 ViewModelStore 封装在一个特殊对象中保存并在 FragmentActivity 的 onRetainNonConfigurationInstance() 方法中返回即可：</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs"><span class="hljs-comment">/**
 * Called by the system, as part of destroying an
 * activity due to a configuration change, when it is known that a new
 * instance will immediately be created for the new configuration.  You
 * can return any object you like here, including the activity instance
 * itself, which can later be retrieved by calling
 * {<span class="hljs-doctag">@link</span> #getLastNonConfigurationInstance()} in the new activity
 * instance.
 */</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-meta">@Nullable</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> Object <span class="hljs-title function_">onRetainNonConfigurationInstance</span><span class="hljs-params">()</span> {
    <span class="hljs-type">Object</span> <span class="hljs-variable">custom</span> <span class="hljs-operator">=</span> onRetainCustomNonConfigurationInstance();
    <span class="hljs-type">ViewModelStore</span> <span class="hljs-variable">viewModelStore</span> <span class="hljs-operator">=</span> mViewModelStore;

    <span class="hljs-comment">// ...省略与原理无关代码</span>

    <span class="hljs-type">NonConfigurationInstances</span> <span class="hljs-variable">nci</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">NonConfigurationInstances</span>();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    <span class="hljs-keyword">return</span> nci;
}
</code></pre>
<p>这样，在顶层源头上就保证了所有 Controller 的 ViewModels 不会在发送配置变化的时候由于 Controller 重建而被销毁。</p>
<p>另外在 Fragment 层中，必须区分 Fragment 实例销毁时到底是因为调用了 onDestroy 还是配置发生了变化，如果是前者则必须清理自身持有的 ViewModelStore，如果是后者则不能清理：</p>
<p><img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/2467709-20210717130146827-91061972.png" alt="" loading="lazy" class="medium-zoom-image"></p>
<p>如图所示，也说明了 Fragment 的 ViewModel 生命周期与该 Fragment 生命周期是一致的。</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs"><span class="hljs-comment">// FragmentManagerImpl.moveToState()</span>
<span class="hljs-comment">//...省略</span>
<span class="hljs-type">boolean</span> <span class="hljs-variable">beingRemoved</span> <span class="hljs-operator">=</span> f.mRemoving &amp;&amp; !f.isInBackStack(); <span class="hljs-comment">// 是否 destroy，如果只是配置变化，则为 false</span>
<span class="hljs-keyword">if</span> (beingRemoved || mNonConfig.shouldDestroy(f)) {
    <span class="hljs-type">boolean</span> shouldClear;
    <span class="hljs-keyword">if</span> (mHost <span class="hljs-keyword">instanceof</span> ViewModelStoreOwner) {
        <span class="hljs-comment">// 说明这是第一层 Fragment，只要顶层 ViewModelStore 没有清除该 FragmentManagerViewModel 就说明不用清理</span>
        shouldClear = mNonConfig.isCleared();
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (mHost.getContext() <span class="hljs-keyword">instanceof</span> Activity) {
        <span class="hljs-comment">// 说明是 FragmentActivity 的子孙 Fragment，根据是否是配置变化来判断是否需要清理</span>
        <span class="hljs-type">Activity</span> <span class="hljs-variable">activity</span> <span class="hljs-operator">=</span> (Activity) mHost.getContext();
        shouldClear = !activity.isChangingConfigurations();
    } <span class="hljs-keyword">else</span> {
        shouldClear = <span class="hljs-literal">true</span>;
    }
    <span class="hljs-keyword">if</span> (beingRemoved || shouldClear) {
        <span class="hljs-comment">// 只有确实 destroy 了才需要清理</span>
        mNonConfig.clearNonConfigState(f);
    }
    f.performDestroy();
    dispatchOnFragmentDestroyed(f, <span class="hljs-literal">false</span>);
}
<span class="hljs-comment">//...省略</span>
</code></pre>
<br>
<br>
<h3 id="4-fragmentactivity-中的-viewmodel-生命周期">4 FragmentActivity 中的 ViewModel 生命周期<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h3>
<p>最后，我们还需要说明一下，FragmentActivity 中的 ViewModel 的生命周期是如何保持与 FragmentActivity 一致的，除了上一节中 FragmentActivity.onRetainNonConfigurationInstance() 中的配置无关保证以外，还需要保证在 Activity 真正销毁的时候其所持有的 ViewModel 也应该被清理。</p>
<p>其代码实现非常简单，只需要观察该 Activity 的 Lifecycle 状态，并在销毁状态时进行清理即可，关于 Lifecycle 我们将用专门的章节进行说明，以下为清理代码：</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs">getLifecycle().addObserver(<span class="hljs-keyword">new</span> <span class="hljs-title class_">LifecycleEventObserver</span>() {
    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">onStateChanged</span><span class="hljs-params">(<span class="hljs-meta">@NonNull</span> LifecycleOwner source,
            <span class="hljs-meta">@NonNull</span> Lifecycle.Event event)</span> {
        <span class="hljs-keyword">if</span> (event == Lifecycle.Event.ON_DESTROY) {
            <span class="hljs-comment">// 观察到 Activity 被销毁</span>
            <span class="hljs-keyword">if</span> (!isChangingConfigurations()) {
                <span class="hljs-comment">// 若不是配置变化，则清理</span>
                getViewModelStore().clear();
            }
        }
    }
});
</code></pre>
<br>
<br>
<h3 id="5-多-controller-共享-viewmodel">5 多 Controller 共享 ViewModel<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h3>
<p>我们参考第3.1节的 ViewModelStore 树可知，如果多个 Controller 需要共享同一个 ViewModel 的话，我们只需要将该 ViewModel 保存在这些 Controller 共同的父 Controller 的 ViewModelStore 中即可，而这些子 Controller 可以通过如下方式获取这个共享的 ViewModel：</p>
<pre class="highlighter-hljs" highlighted="true"><code class="language-java highlighter-hljs hljs">[Fragment/FragmentActivity] parentContrl = ... <span class="hljs-comment">// 共同的父 Controller</span>
<span class="hljs-keyword">final</span> <span class="hljs-type">CommonViewModel</span> <span class="hljs-variable">viewModel</span> <span class="hljs-operator">=</span> ViewModelProviders.of(parentContrl).get(CommonViewModel.class);
</code></pre>
<p>这样我们就解决了在 1.1 节中提到的第二个场景共享数据的问题。</p>
<br>
<br>
<h3 id="6-关于工厂模式的一点思考">6 关于工厂模式的一点思考<button class="cnblogs-toc-button" title="显示目录导航" aria-expanded="false"></button></h3>
<p>回到第2节中如何获取一个 ViewModel 实例的过程，我们发现，ViewModelProviders 实际相当于一个 <strong>简单工厂</strong> 模式，而 Facotry 则是一个 <strong>工厂方法</strong> 模式，前者根据不同的参数构造不同的 ViewModelProvider，后者则可以实现不同的具体 Factory 来构造不同的 ViewModel。</p>
<p>这里有两层抽象：</p>
<ul>
<li>如何实现并实例化一个数据模型，各个数据模型的实现细节是不可预知的 -&gt; ViewModel；</li>
<li>采用什么样的规则去得到一个 ViewModel 实例，且该规则是一个统一可预知的规则，但并不关心 ViewModel 实现的细节 -&gt; ViewModelProvider。</li>
</ul>
<p>所以我们看到，当一个对象的构造是采用了统一的规则时（比如 ViewModelProvider），适合用简单工厂模式来实现，因为该规则本身可以被封装；而当一个对象的构造方式没有统一规则可以遵循，其实现细节更多与业务相关时，其可被封装的部分仅为它的 new 方法，这时更适合用工厂方法模式来实现。</p>
<br>
<br>
<br>
<blockquote>
<p>说明：</p>
<p>本文参考的 androidx 版本为</p>
<p>core: 1.1.0</p>
<p>lifecyle: 2.2.0-alpha01</p>
<p>fragment: 1.1.0-alpha09</p>
</blockquote>
<br>
<p><strong>如果大家喜欢我写的文章，欢迎关注我的公众号——小舍，将有更多有趣的内容分享给大家：</strong></p>
<p><img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/2467709-20210717130715340-1389235610.jpg" alt="" loading="lazy" class="medium-zoom-image"></p>

</div>
<div class="clear"></div>
<div id="blog_post_info_block" role="contentinfo">



<div id="EntryTag">
    标签: 
            <a href="https://www.cnblogs.com/cubbychat/tag/androidx/">androidx</a>
,             <a href="https://www.cnblogs.com/cubbychat/tag/ViewModel/">ViewModel</a>
,             <a href="https://www.cnblogs.com/cubbychat/tag/MVVM/">MVVM</a>
,             <a href="https://www.cnblogs.com/cubbychat/tag/Android/">Android</a>
</div>
    <div id="blog_post_info">
<div id="green_channel">
        <a href="javascript:void(0);" id="green_channel_digg" onclick="DiggIt(15023207,cb_blogId,1);green_channel_success(this,&#39;谢谢推荐！&#39;);">好文要顶</a>
        <a id="green_channel_follow" onclick="follow(&#39;93f07b19-c65c-437c-168b-08d946b6fb4b&#39;);" href="javascript:void(0);">关注我</a>
    <a id="green_channel_favorite" onclick="AddToWz(cb_entryId);return false;" href="javascript:void(0);">收藏该文</a>
    <a id="green_channel_wechat" href="javascript:void(0);" onclick="shareManager.wechatShare()">微信分享</a>
</div>
<div id="author_profile">
    <div id="author_profile_info" class="author_profile_info">
            <a href="https://home.cnblogs.com/u/cubbychat/" target="_blank"><img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/20210717121437.png" class="author_avatar" alt=""></a>
        <div id="author_profile_detail" class="author_profile_info">
            <a href="https://home.cnblogs.com/u/cubbychat/">码农小舍</a>
            <br>
            <a href="https://home.cnblogs.com/u/cubbychat/followers/">粉丝 - <span class="follower-count">0</span></a>
            <a href="https://home.cnblogs.com/u/cubbychat/followees/">关注 - <span class="following-count">0</span></a><br>
        </div>
    </div>
    <div class="clear"></div>
    <div id="author_profile_honor"></div>
    <div id="author_profile_follow" class="follow-tip">
                <a href="javascript:void(0);" onclick="follow(&#39;93f07b19-c65c-437c-168b-08d946b6fb4b&#39;);return false;">+加关注</a>
    </div>
</div>
<div id="div_digg">
    <div class="diggit" onclick="votePost(15023207,&#39;Digg&#39;)">
        <span class="diggnum" id="digg_count">0</span>
    </div>
    <div class="buryit" onclick="votePost(15023207,&#39;Bury&#39;)">
        <span class="burynum" id="bury_count">0</span>
    </div>
    <div class="clear"></div>
    <div class="diggword" id="digg_tips">
    </div>
        <div id="vip_tip"><a class="tip" href="https://cnblogs.vip/" target="_blank">升级成为会员</a></div>
</div>

<script type="text/javascript">
    currentDiggType = 0;
</script>
</div>
    <div class="clear"></div>
    <div id="post_next_prev">


</div>
</div>
            </div>
            <div class="postDesc">posted @ 
<span id="post-date" data-last-update-days="1036.3951586921296" data-date-created="BlogServer.Application.Dto.BlogPost.BlogPostDto" data-date-updated="2021-07-17 13:10" aria-expanded="false">2021-07-17 13:07</span>&nbsp;
<a href="https://www.cnblogs.com/cubbychat">码农小舍</a>&nbsp;
阅读(<span id="post_view_count">2039</span>)&nbsp;
评论(<span id="post_comment_count">0</span>)&nbsp;
<a href="https://i.cnblogs.com/EditPosts.aspx?postid=15023207" rel="nofollow">编辑</a>&nbsp;
<a href="javascript:void(0)" onclick="AddToWz(15023207);return false;">收藏</a>&nbsp;
<a href="javascript:void(0)" onclick="reportManager.report({ currentUserId: &#39;&#39;, targetType: &#39;blogPost&#39;, targetId: &#39;15023207&#39;, targetLink: &#39;https://www.cnblogs.com/cubbychat/p/15023207.html&#39;, title: &#39;Android mvvm框架之ViewModel原理&#39; })">举报</a>
</div>
        </div>
        <script>
    var cb_entryId = 15023207, cb_entryCreatedDate = '2021-07-17 13:07', cb_postType = 1, cb_postTitle = 'Android mvvm框架之ViewModel原理';
    var allowComments = true, cb_blogId = 694622, cb_blogApp = 'cubbychat', cb_blogUserGuid = '93f07b19-c65c-437c-168b-08d946b6fb4b';
    mermaidRender.render()
    markdown_highlight()
    zoomManager.apply("#cnblogs_post_body img:not(.code_img_closed):not(.code_img_opened)");    
</script>
        <a id="!comments"></a>
<div id="blog-comments-placeholder"></div>
<div id="comment_form" class="commentform">
    <a name="commentform"></a>
    <div id="divCommentShow"></div>
    <div id="comment_nav">
            <a class="comment-nav-left forpc" href="https://cnblogs.vip/" target="_blank">会员力量，点亮园子希望</a>
        <div class="comment-nav-right">
            <span id="span_refresh_tips"></span><a href="https://www.cnblogs.com/cubbychat/p/15023207.html#" onclick="return RefreshPage();">刷新页面</a><a href="https://www.cnblogs.com/cubbychat/p/15023207.html#top">返回顶部</a>
        </div>
    </div>
    <div id="comment_form_container"><div class="login_tips">
    登录后才能查看或发表评论，立即 <a rel="nofollow" href="javascript:void(0);" class="underline" onclick="return account.login(&#39;!comments&#39;);">登录</a> 或者
    <a href="https://www.cnblogs.com/">逛逛</a> 博客园首页
</div>
</div>
    <div class="ad_text_commentbox" id="ad_text_under_commentbox"></div>
        <div id="cnblogs_ch"><a href="https://www.cnblogs.com/sanshi/p/18186007" target="_blank" onclick="gtag(&#39;event&#39;, &#39;click&#39;, {&#39;event_category&#39;: &#39;ad&#39;, &#39;event_label&#39;: &#39;T2-三生石上&#39;})">【推荐】在 ASP.NET Core 中运行 WebForms 业务代码</a><br><a href="https://www.cnblogs.com/cmt/p/18094172" target="_blank" onclick="gtag(&#39;event&#39;, &#39;click&#39;, {&#39;event_category&#39;: &#39;ad&#39;, &#39;event_label&#39;: &#39;T2-博客园-周边&#39;})">【推荐】园子周边第二季：更大的鼠标垫，没有logo的鼠标垫</a><br><a href="https://market.cnblogs.com/" target="_blank" onclick="gtag(&#39;event&#39;, &#39;click&#39;, {&#39;event_category&#39;: &#39;ad&#39;, &#39;event_label&#39;: &#39;T2-云市场商店&#39;})">【推荐】阿里云云市场联合博客园推出开发者商店，欢迎关注</a><br><a href="https://cnblogs.vip/buy" target="_blank" onclick="gtag(&#39;event&#39;, &#39;click&#39;, {&#39;event_category&#39;: &#39;ad&#39;, &#39;event_label&#39;: &#39;T2-博客园-会员&#39;})">【推荐】会员力量，点亮园子希望，期待您升级成为园子会员</a><br></div>
    <div id="opt_under_post"></div>
        <div id="cnblogs_c1" class="under-post-card">
            <a href="https://cnblogs.vip/buy" rel="nofollow" target="_blank" onclick="countCreativeClicks(&#39;C1-博客园-会员&#39;)">
                <img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/35695-20240328092537879-1189167464.jpg" onload="countCreativeImpressions(&#39;C1-博客园-会员&#39;)" alt="">
                <span id="c1_impression" style="display:none"></span>
            </a>
        </div>
    <div id="under_post_card1"><div class="under-post-card">
<b>编辑推荐：</b>
<br>

· <a href="https://www.cnblogs.com/apocelipes/p/18195214" target="_blank">因为我的一次疏忽而带来的 golang1.23 新特性</a>
    <br>
· <a href="https://www.cnblogs.com/vipwan/p/18194062/biwen-quickapi-scheduletask" target="_blank">NETCore 中实现一个轻量无负担的极简任务调度 ScheduleTask</a>
    <br>
· <a href="https://www.cnblogs.com/czwy/p/18192720" target="_blank">WPF 使用 Shape 实现复杂线条动画</a>
    <br>
· <a href="https://www.cnblogs.com/gmmy/p/18186750" target="_blank">使用 c# 强大的表达式树实现对象的深克隆</a>
    <br>
· <a href="https://www.cnblogs.com/alchemystar/p/18188082" target="_blank">日常 Bug 排查 - 连接突然全部关闭</a>
    <br>
</div></div>
    <div id="under_post_card2"><div class="itnews under-post-card">
    <b>阅读排行：</b>
    <br>
 ·          <a href="https://www.cnblogs.com/shanyou/p/18197134" target="_blank">Dapr 与 .NET Aspire 结合使用获得无与伦比的本地开发体验</a>
        <br>
 ·          <a href="https://www.cnblogs.com/edisonchou/p/-/industrial-control-plc-connector-02" target="_blank">初识上位机（下）：C#读写PLC数据块数据</a>
        <br>
 ·          <a href="https://www.cnblogs.com/Can-daydayup/p/18197033" target="_blank">ASP.NET Core Web中使用AutoMapper进行对象映射</a>
        <br>
 ·          <a href="https://www.cnblogs.com/weskynet/p/18198774" target="_blank">工业福利！用.NET快速开发物联网扫码器设备的通用扫码功能</a>
        <br>
 ·          <a href="https://www.cnblogs.com/zhangapple/p/18197240" target="_blank">基于Vue3水印组件封装：防篡改守护！</a>
        <br>
</div></div>
    <div id="HistoryToday" class="under-post-card"></div>
    <script type="text/javascript">
        var commentManager = new blogCommentManager();
        commentManager.renderComments(0);
        fixPostBody();
        window.footnoteTipManager.generateFootnoteTips();

            window.tocManager.displayDisableTocTips = false;
            window.tocManager.generateToc();
            
                setTimeout(function() { countViews(cb_blogId, cb_entryId); }, 50);
            
            deliverT2();
            deliverC1C2();
            loadNewsAndKb();
            
                LoadPostCategoriesTags(cb_blogId, cb_entryId);
            
            LoadPostInfoBlock(cb_blogId, cb_entryId, cb_blogApp, cb_blogUserGuid);
            GetPrevNextPost(cb_entryId, cb_blogId, cb_entryCreatedDate, cb_postType);
            loadOptUnderPost();
            GetHistoryToday(cb_blogId, cb_blogApp, cb_entryCreatedDate);
                </script>
</div>

    </div>
</div>
            </div>
        </div>

        <div id="sideBar">
            <div id="sideBarMain">
                <div id="sidebar_news" class="newsItem">
    
<h3 class="catListTitle">公告</h3>
<div id="blog-news">    
    <div id="sidebar_news_content">
<div id="profile_block">
    昵称：
    <a href="https://home.cnblogs.com/u/cubbychat/">
        码农小舍
    </a>
    <br>
    园龄：
    <a href="https://home.cnblogs.com/u/cubbychat/" title="入园时间：2021-07-17">
        2年10个月
    </a>
    <br>
    粉丝：
    <a class="follower-count" href="https://home.cnblogs.com/u/cubbychat/followers/">
        0
    </a>
    <br>
    关注：
    <a class="folowing-count" href="https://home.cnblogs.com/u/cubbychat/followees/">
        0
    </a>
    <div id="p_b_follow" class="follow-tip">
<a href="javascript:void(0)" onclick="follow(&#39;93f07b19-c65c-437c-168b-08d946b6fb4b&#39;)">+加关注</a></div>
    <script>getFollowStatus('93f07b19-c65c-437c-168b-08d946b6fb4b');</script>
</div>
</div>
</div>
<script>loadBlogNews();</script> 
</div>
<div id="sidebar_c3"></div>
                <div id="calendar"><div id="blog-calendar" style="">

<table id="blogCalendar" class="Cal" cellspacing="0" cellpadding="0" title="Calendar" border="0">
    <tbody>
        <tr>
            <td colspan="7">
                <table class="CalTitle" cellspacing="0" border="0">
                    <tbody>
                        <tr>
                            <td class="CalNextPrev">
                                <a href="javascript:void(0);" onclick="loadBlogCalendar(&#39;2024/04/18&#39;); return false;">&lt;</a>
                            </td>
                            <td align="center">2024年5月</td>
                            <td align="right" class="CalNextPrev">
                                <a href="javascript:void(0);" onclick="loadBlogCalendar(&#39;2024/06/18&#39;); return false;">&gt;</a>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </td>
        </tr>
    <tr>
        <th class="CalDayHeader" align="center" abbr="日" scope="col">日</th>
        <th class="CalDayHeader" align="center" abbr="一" scope="col">一</th>
        <th class="CalDayHeader" align="center" abbr="二" scope="col">二</th>
        <th class="CalDayHeader" align="center" abbr="三" scope="col">三</th>
        <th class="CalDayHeader" align="center" abbr="四" scope="col">四</th>
        <th class="CalDayHeader" align="center" abbr="五" scope="col">五</th>
        <th class="CalDayHeader" align="center" abbr="六" scope="col">六</th>
    </tr>
            <tr>
                            <td class="CalOtherMonthDay" align="center">28</td>
                            <td class="CalOtherMonthDay" align="center">29</td>
                            <td class="CalOtherMonthDay" align="center">30</td>
                        <td class="" align="center">
                            1
                        </td>
                        <td class="" align="center">
                            2
                        </td>
                        <td class="" align="center">
                            3
                        </td>
                    <td class="CalWeekendDay" align="center">
                        4
                    </td>
            </tr>
                <tr>
                        <td class="CalWeekendDay" align="center">
                            5
                        </td>
                            <td class="" align="center">
                                6
                            </td>
                            <td class="" align="center">
                                7
                            </td>
                            <td class="" align="center">
                                8
                            </td>
                            <td class="" align="center">
                                9
                            </td>
                            <td class="" align="center">
                                10
                            </td>
                        <td class="CalWeekendDay" align="center">
                            11
                        </td>
                </tr>
                <tr>
                        <td class="CalWeekendDay" align="center">
                            12
                        </td>
                            <td class="" align="center">
                                13
                            </td>
                            <td class="" align="center">
                                14
                            </td>
                            <td class="" align="center">
                                15
                            </td>
                            <td class="" align="center">
                                16
                            </td>
                            <td class="" align="center">
                                17
                            </td>
                        <td class="CalTodayDay" align="center">
                            18
                        </td>
                </tr>
                <tr>
                        <td class="CalWeekendDay" align="center">
                            19
                        </td>
                            <td class="" align="center">
                                20
                            </td>
                            <td class="" align="center">
                                21
                            </td>
                            <td class="" align="center">
                                22
                            </td>
                            <td class="" align="center">
                                23
                            </td>
                            <td class="" align="center">
                                24
                            </td>
                        <td class="CalWeekendDay" align="center">
                            25
                        </td>
                </tr>
                <tr>
                        <td class="CalWeekendDay" align="center">
                            26
                        </td>
                            <td class="" align="center">
                                27
                            </td>
                            <td class="" align="center">
                                28
                            </td>
                            <td class="" align="center">
                                29
                            </td>
                            <td class="" align="center">
                                30
                            </td>
                            <td class="" align="center">
                                31
                            </td>
                        <td class="CalOtherMonthDay" align="center">
                            1
                        </td>
                </tr>
                <tr>
                        <td class="CalOtherMonthDay" align="center">
                            2
                        </td>
                            <td class="CalOtherMonthDay" align="center">
                                3
                            </td>
                            <td class="CalOtherMonthDay" align="center">
                                4
                            </td>
                            <td class="CalOtherMonthDay" align="center">
                                5
                            </td>
                            <td class="CalOtherMonthDay" align="center">
                                6
                            </td>
                            <td class="CalOtherMonthDay" align="center">
                                7
                            </td>
                        <td class="CalOtherMonthDay" align="center">
                            8
                        </td>
                </tr>
    </tbody>
</table></div></div>                
                <script>loadBlogDefaultCalendar();</script>
                <div id="leftcontentcontainer">
                    <!-- begin:SingleColumn -->
                    <div id="blog-sidecolumn"><!-- 搜索 -->
<div id="sidebar_search" class="sidebar-block">
    <div class="mySearch my-search">
        <h3 class="catListTitle">搜索</h3>
        <div id="sidebar_search_box">
            <div id="widget_my_zzk" class="div_my_zzk">
                <input type="text" id="q" onkeydown="return zzk_go_enter(event);" class="input_my_zzk">&nbsp;<input onclick="zzk_go()" type="button" value="找找看" id="btnZzk" class="btn_my_zzk">
            </div>
            
        </div>
    </div>
</div>

<!-- 常用链接 -->
<div id="sidebar_shortcut" class="sidebar-block">
<h3 class="catListTitle">
常用链接
</h3>
<ul>
    <li><a href="https://www.cnblogs.com/cubbychat/p/" title="我的博客的随笔列表">我的随笔</a></li>
<li><a href="https://www.cnblogs.com/cubbychat/MyComments.html" title="我的发表过的评论列表">我的评论</a></li>
<li><a href="https://www.cnblogs.com/cubbychat/OtherPosts.html" title="我评论过的随笔列表">我的参与</a></li>
<li><a href="https://www.cnblogs.com/cubbychat/comments" title="我的博客的评论列表">最新评论</a></li>
<li><a href="https://www.cnblogs.com/cubbychat/tag/" title="我的博客的标签列表">我的标签</a></li>

</ul>
</div>

<!-- 最新随笔 -->


<!-- 我的标签 -->
<div id="sidebar_toptags" class="sidebar-block"><div class="catListTag">
    <h3 class="catListTitle"><a href="https://www.cnblogs.com/cubbychat/tag/" class="sidebar-card-title-a">我的标签</a></h3>
    <ul>
                <li>
            <a href="https://www.cnblogs.com/cubbychat/tag/ViewModel/">ViewModel<span class="tag-count">(1)</span></a>
        </li>
        <li>
            <a href="https://www.cnblogs.com/cubbychat/tag/MVVM/">MVVM<span class="tag-count">(1)</span></a>
        </li>
        <li>
            <a href="https://www.cnblogs.com/cubbychat/tag/androidx/">androidx<span class="tag-count">(1)</span></a>
        </li>
        <li>
            <a href="https://www.cnblogs.com/cubbychat/tag/Android/">Android<span class="tag-count">(1)</span></a>
        </li>
    

    </ul>
</div>
</div>

<!-- 积分与排名 -->


<!-- 随笔分类、随笔档案、文章分类、新闻分类、相册、链接 -->
<div id="sidebar_categories">

    <div class="catListPostArchive">
        <h3 class="catListTitle">
            
随笔档案


        </h3>
        <ul>
                <li data-category-list-item-visible="true" style="display: block">
                    
<a href="https://www.cnblogs.com/cubbychat/p/archive/2021/07" style="--cnb-category-item-link-indent-size: calc(0 * 1em)" class="category-item-link" rel="" target="">2021年7月(1)</a>

                    
                </li>
                                        
        </ul>
    </div></div>

<!-- 最新评论 -->
<!-- 阅读排行榜 -->
<div id="sidebar_topviewedposts" class="sidebar-block">
<div id="topview_posts_wrap">
    <h3 class="catListTitle">
        <a href="https://www.cnblogs.com/cubbychat/most-viewed" class="sidebar-card-title-a">
    阅读排行榜
</a>

    </h3>
    <div id="TopViewPostsBlock">
        <ul style="word-break:break-all">
                    <li>
                        <a href="https://www.cnblogs.com/cubbychat/p/15023207.html">
                            1. Android mvvm框架之ViewModel原理(2038)
                        </a>
                    </li>
        </ul>
    </div>
</div></div>

<!-- 评论排行榜 -->
<div id="sidebar_topcommentedposts" class="sidebar-block"></div>

<!-- 推荐排行榜 -->
<div id="sidebar_topdiggedposts" class="sidebar-block"></div><div id="sidebar_recentcomments" class="sidebar-block"></div>


</div>
                    <script>loadBlogSideColumn();</script>
                    <!-- end:  SingleColumn -->
                </div>
            </div>
        </div>
        <div class="clear"></div>
    </div>
    <div class="clear"></div>
    <div id="footer">
        <!--done-->
Copyright © 2024 码农小舍
<br><span id="poweredby">Powered by .NET 8.0 on Kubernetes</span>

    </div>
</div>


    

    <input type="hidden" id="antiforgery_token" value="CfDJ8C838EyK0EpKpQQcC9VwxBnvjxzT1H2W2Uz2uNQFBHWLiU7GK0y2lRKIiSNWc4Tey4N4GdwRG39zM7OZNyarVm8OagDYJ4hG899W36zUwYcDyWF1YJva8ifJ3H7ihTe_ctkf6FzAYpTXgmUyKXKeNpM">
    <script async="" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/js"></script>
<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() { dataLayer.push(arguments); }
    gtag('js', new Date());
    gtag('config', 'G-M95P3TTWJZ');
</script>
<script defer="" src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/hm.js.下载"></script>


<div style="z-index: 998; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.6); display: none; flex-direction: column; align-items: center; justify-content: center">
<div style="background: #fff;padding: 30px; margin: 30px; border-radius: 16px;">
  <div style="width: 200px; height: 200px; background: rgba(255, 255, 255, 0.8); position: absolute; top: -100px; right: -100px; border-radius: 100px"></div>
  <div style="text-align: center; font-size: 18px; margin-bottom: 10px;">点击右上角即可分享</div>
  <img src="./Android mvvm框架之ViewModel原理 - 码农小舍 - 博客园_files/35695-20230906145857937-1471873834.gif" alt="微信分享提示">
</div>   
    </div></body></html>